﻿var http = require('http')
    , https = require('https')
    , url = require('url')
    , iconv = require('iconv-lite')
    , mockjs = require('mockjs')
    , fs = require('fs')
    , path = require('path')
    , BufferHelper = require('bufferhelper');

var STATUS_MOCK = 'mock';
var ENCODING_RAW = 'raw';

/**
 * 请求代理类
 * @param {InterfacesManager} interfacesManager 接口解析对象
 * @param {bool} debug 
 */
var Proxy = function (interfacesManager,debug){
    this._interfaces = interfacesManager;
    this.debug = !!debug;
}

//是否调试
Proxy.prototype.debug = false;

/**
 * 返回全接口配置信息
 * @return {object}
 */
Proxy.prototype.Interfaces = function (){
    return this._interfaces;
}

/**
 * 请求
 * @param {string} interfaceId 接口ID
 * @param {object} params 请求参数
 * @param {function} callback 请求结果回调函数
 * @param {function} errCallback 错误结果回调函数
 * @param {object} cookies cookie
 * @param {object} headers 附加的头部信息
 */
Proxy.prototype.request = function (interfaceId, params, callback, errCallback, cookies, headers) {
    if (typeof callback !== 'function') {
        this.debug && console.error('callback 需是一个函数 ' + interfaceId);
        return;
    }
    errCallback = typeof errCallback !== 'function' 
                    ? function (e) { console.error(e); }
                    : errCallback;
    var that = this;
    var profile = that._getInterface(interfaceId);
    if (profile === undefined) //获取不到接口信息直接返回
        return;
    if (profile.cookies && cookies === undefined) {
        throw new Error(interfaceId + " 需要附带cookie信息，请指定");
    }
    if (profile.params) {
        params = params || {}; //默认空参数
        for (var key in profile.params) {
            if (!params[key])
                throw new Error(interfaceId + " 缺少必要参数 " + key);
                //参数内容匹配暂时未作
        }
    }
    if (profile.status === STATUS_MOCK) { //mock
        that._mockRequest(profile, callback, errCallback);
        return;
    }
    that.debug && console.log("["+interfaceId+"]接口请求：" + profile.url);
    //请求地址解析
    var urlObj = url.parse(profile.url);
    profile.hostname = urlObj.hostname;
    profile.port = urlObj.port || 80;
    profile.path = urlObj.path;
    
    var options = {
        hostname: profile.hostname,
        port: profile.port,
        path: profile.path,
        method: profile.method,
        headers: headers || {}
    };
    var querystring = that._queryStringify(params);
    // Set cookie
    if (cookies) { //如果cookie存在设置cookie
        options.headers['Cookie'] = cookies;
    }
    if (profile.method === 'POST') {
        options.headers[ 'Content-Type' ] = 'application/x-www-form-urlencoded';
        options.headers[ 'Content-Length' ] = querystring.length;
    } else if (profile.method === 'GET') {
        options.path += '?' + querystring;
    }
    //请求返回处理
    var cb = function (res){
        var bufferHelper = new BufferHelper();
        res.on('data', function (chunk) {
            bufferHelper.concat(chunk);
        });
        res.on('end', function () {
            var buffer = bufferHelper.toBuffer();
            that.debug && console.log(iconv.fromEncoding(buffer, profile.encoding));
            try {
                var result = profile.encoding === ENCODING_RAW 
                        ? buffer
                        : (profile.dataType !== 'json' 
                            ? iconv.fromEncoding(buffer, profile.encoding)
                            : JSON.parse(iconv.fromEncoding(buffer, profile.encoding)));
            } catch (e) {
                clearTimeout(timer);
                errCallback(new Error("请求失败. " + e));
                return;
            }
            clearTimeout(timer);
            callback(result, res.headers['set-cookie']);
        });
    }
    if (profile.url.indexOf("https://") === 0) {
        options.rejectUnauthorized = !!profile.rejectUnauthorized;
        options.port = 443;
        if (profile.pfx) {
            if (profile.passphrase) {
                options.pfx = fs.readFileSync(profile.pfx);
                options.passphrase = profile.passphrase;
            } else {
                that.debug && console.warn("pfx证书未设置证书密码");
            }
        } else if (profile.cert) {
            options.cert = fs.readFileSync(profile.cert);
            if (profile.key)
                options.key = fs.readFileSync(profile.key);
            else
                that.debug && console.warn("cert证书未设置证书密钥");
        }
        var req = https.request(options, function (res) { 
            cb(res);
        });
    } else {
        var req = http.request(options, function (res) {
            cb(res);
        });
    }
    profile.method !== 'POST' || req.write(querystring);
    req.on('error', function (e) {
        errCallback(e);
    });
    var timer = setTimeout(function () {
        errCallback(new Error('timeout'));
    }, profile.timeout || 5000);
    req.end();
}

/**
 * 处理mock请求
 * @param {object} profile 接口文件地址
 * @param {function} callback 处理完成回调函数
 * @param {errCallback} errCallback 错误处理函数
 * @return object
 */
Proxy.prototype._mockRequest = function (profile, callback, errCallback) {
    try {
        var data = require(path.join(process.cwd(), profile.url));
        callback(mockjs.mock(data));
    } catch (e) {
        errCallback(e);
    }
}

/**
 * 生成请求参数
 * @param {object} params
 * @return string
 */
Proxy.prototype._queryStringify = function(params){
    if (!params || typeof params === 'string') {
        return params || '';
    } else if (params instanceof Array) {
        return params.join('&');
    }
    var qs = [], val;
    for (var i in params) {
        val = typeof params[i] === 'object' 
                ? JSON.stringify(params[ i ])
                : params[ i ];
        qs.push(i + '=' + encodeURIComponent(val));
    }
    return qs.join('&');
}

/**
 * 获取接口信息
 * @param {string} interfaceId 接口ID
 * @return {object}
 */
Proxy.prototype._getInterface = function (interfaceId) {
    if (this._interfaces !== undefined) {
        try {
            return this._interfaces.getInterface(interfaceId);
        } catch (e) {
            this.debug && console.error("接口未找到" + interfaceId);
        }
    }
    return undefined;
}

/**
 * 直接代理请求
 * @param {string} interfaceid 接口ID
 * @param {httprequest} req
 * @param {httpresponse} res
 * @param {Function} callback 请求返回
 */
Proxy.prototype.Proxy = function (interfaceid,req, res,callback){
    var that = this;
    var profile = that._getInterface(interfaceid);
    if (profile === undefined) { //获取不到接口信息直接返回
        res.setHeader('Content-Type', 'text/html;charset=UTF-8');
        res.statusCode = 404;
        res.end('未找到接口地址: ' + req.url + '\n' );
        return;
    }
    if (profile.status === STATUS_MOCK) { //mock
        that._mockRequest(profile, function (data) { 
            res.setHeader('Content-Type', (profile.dataType === 'json' ? 'application/json' : 'text/html')+ ';charset=UTF-8');
            res.end(JSON.stringify(data));
        }, function (err) { 
            res.statusCode = 500;
            res.end(err + '');
        });
        return;
    }
    that.debug && console.log("[" + interfaceid + "]接口请求：" + profile.url);
    //请求地址解析
    var urlObj = url.parse(profile.url);
    profile.hostname = urlObj.hostname;
    profile.port = urlObj.port || 80;
    profile.path = urlObj.path;

    var options = {
        hostname: profile.hostname,
        port: profile.port,
        path: profile.path + '?' + req.url.replace(/^[^\?]*\?/, ''),
        method: profile.method,
        headers: req.headers
    };
    options.headers.host = profile.hostname;
    // delete options.headers.referer;
    // delete options.headers['x-requested-with'];
    // delete options.headers['connection'];
    // delete options.headers['accept'];
    delete options.headers['accept-encoding'];
    
    var cb = function (res2){
        if (callback) {
            callback(res2); //通过回调函数处理请求结果
        } else {
            if (res2.headers["set-cookie"])
                res.setHeader("Set-Cookie", res2.headers["set-cookie"]);
            res.setHeader('Content-Type', res2.headers['content-type'] || ((profile.dataType === 'json' ? 'application/json' : 'text/html') + ';charset=UTF-8'));
            res2.pipe(res);
        }
    }
    if (profile.url.indexOf("https://") === 0) {
        options.rejectUnauthorized = !!profile.rejectUnauthorized;
        options.port = 443;
        if (profile.pfx) {
            if (profile.passphrase) {
                options.pfx = fs.readFileSync(profile.pfx);
                options.passphrase = profile.passphrase;
            } else {
                that.debug && console.warn("pfx证书未设置证书密码");
            }
        } else if (profile.cert) {
            options.cert = fs.readFileSync(profile.cert);
            if (profile.key)
                options.key = fs.readFileSync(profile.key);
            else
                that.debug && console.warn("cert证书未设置证书密钥");
        }
        var req2 = https.request(options, function (res2) {
            cb(res2);
        });
    } else {
        var req2 = http.request(options, function (res2) {
            cb(res2);
        });
    }
    req2.on('error', function (e) {
        res.setHeader('Content-Type', 'text/html;charset=UTF-8');
        res.statusCode = 500;
        res.end(e + '');
    });
    req.pipe(req2);
}

module.exports = Proxy;